package com.gmail.mariska.martin;

import javax.persistence.EntityManager;

import org.apache.log4j.Logger;

import com.gmail.mariska.martin.jpa.Pertask;
import com.gmail.mariska.martin.jpa.PertaskCommand;
import com.gmail.mariska.martin.jpa.dao.BankAccountDAO;
import com.gmail.mariska.martin.model.BankAccount;

public class ThreadDbWorker implements Runnable {
	static final Logger logger = Logger.getLogger(ThreadDbWorker.class);

	int accountIdSource;
	int accountIdDestination;

	public ThreadDbWorker(int accountIdSource, int accountIdDestination) {
		this.accountIdDestination = accountIdDestination;
		this.accountIdSource = accountIdSource;
	}

	@Override
	public void run() {
		logger.info(this + " starts working");
		EntityManager em = null;
		try {
			em = Pertask.getInstance().getEntityManager();

			for (int i = 0; i < 10; i++) {
				sleep();
				boolean needSave = true;
				while (needSave) {
					Boolean ok = Pertask.getInstance().transactionForWrite(new PertaskCommand<Boolean>() {
						private final int amount = 1;

						@Override
						public Boolean execute(Pertask pertask) {
							BankAccountDAO bankAccountDAO = new BankAccountDAO(pertask.getEntityManager());
							BankAccount sourceAccount = bankAccountDAO.find(accountIdSource);
							int value = sourceAccount.getValue();
							sourceAccount.setValue(value - amount);
							bankAccountDAO.update(sourceAccount);

							BankAccount destAccount = bankAccountDAO.find(accountIdDestination);
							value = destAccount.getValue();
							destAccount.setValue(value + amount);
							bankAccountDAO.update(destAccount);
							logger.debug(" moving from(" + sourceAccount.getId() + ") amount " + amount + " to("
									+ destAccount.getId() + ") s:" + sourceAccount + " d:" + destAccount);
							return true;
						}
					});
					needSave = (ok == null) || (ok == false);
				}
			}

		} catch (Exception e) {
			logger.error(e, e);
		} finally {
			if (em.isOpen()) {
				em.close();
			}
		}
	}

	private void sleep() {
		try {
			int randSleepMilis = (int) Math.floor(Math.random() * 500);
			Thread.currentThread().sleep(randSleepMilis);
		} catch (InterruptedException e) {
			logger.error(e, e);
		}
	}
}
